package common;

import java.util.ArrayList;
import java.util.List;

import bean.Distance;
import bean.Point;
import bean.TrafficLights.Lights;

public class BeanUtils {
	/**
	 * 中间点相隔距离
	 */
	private final static int MIN_PIXELS_BETWEEN_TWO_POINTS = 30;
	/**
	 * 路宽
	 */
	private final static int ROAD_WIDTH = 20;

	/**
	 * 求两点距离
	 * 
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static float hypot(Point p1, Point p2) {
		return (float) Math.hypot(p1.getX() - p2.getX(), p1.getY() - p2.getY());
	}

	/**
	 * 求点集的总距离
	 * 
	 * @param ps
	 * @return
	 */
	public static float hypot(Point... ps) {
		if (ps == null)
			return 0f;
		float dis = 0f;
		for (int i = 0; i < ps.length - 1; i++) {
			dis += hypot(ps[i], ps[i + 1]);
		}
		return dis;
	}

	/**
	 * 求两点之间的所有中间点
	 * 
	 * @param p1
	 * @param p2
	 * @param gap
	 * @param size
	 * @return
	 */
	public static List<Point> medianPoints(Point p1, Point p2, int gap, int size) {
		List<Point> ps = new ArrayList<Point>();
		for (int i = 1; i < size + 1; i++) {
			ps.add(median(p1, p2, i * gap));
		}
		return ps;
	}

	/**
	 * 已知两点和中间点距起点距离，求中间点<br>
	 * 交换了p1 p2的位置，此初跟traingle.js中的情况相反
	 * 
	 * @param p2
	 *            终点
	 * @param p1
	 *            起点
	 * @param gap
	 *            距起点距离
	 * @return
	 */
	private static Point median(Point p2, Point p1, int gap) {
		int x1 = p1.getX(), y1 = p1.getY(), x2 = p2.getX(), y2 = p2.getY();
		int _hypot_ = (int) Math.hypot(x2 - x1, y2 - y1);
		int x3 = 0, y3 = 0;
		switch (quadrant(x1, y1, x2, y2)) {
		case 1:
			x3 = x2 - gap * (x2 - x1) / _hypot_;
			y3 = y2 + (gap * (y1 - y2) / _hypot_);
			break;
		case 2:
			x3 = x2 + gap * (x1 - x2) / _hypot_;
			y3 = y2 + gap * (y1 - y2) / _hypot_;
			break;
		case 3:
			x3 = x2 + (gap * (x1 - x2) / _hypot_);
			y3 = y2 - (gap * (y2 - y1) / _hypot_);
			break;
		case 4:
			x3 = x2 - (gap * (x2 - x1) / _hypot_);
			y3 = y2 - (gap * (y2 - y1) / _hypot_);
			break;
		}
		return new Point(0, x3, y3, p1.getArea(), p1.getRoad());
	}

	/**
	 * (x2,y2)相对于(x1,y1)的象限
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @return
	 */
	private static int quadrant(int x1, int y1, int x2, int y2) {
		if (x2 - x1 >= 0 && y2 - y1 >= 0)
			return 4;
		else if (x2 - x1 < 0 && y2 - y1 >= 0)
			return 3;
		else if (x2 - x1 < 0 && y2 - y1 < 0)
			return 2;
		else
			return 1;
	}
	
	/**
	 * 判断p2对于p1的相对方向
	 * @param p1
	 * @param p2
	 * @return
	 */
	public static int direction(Point p1,Point p2){
		if(p1.getX()==p2.getX() || p1.getY()==p2.getY()){
			return Lights.FORWARD;
		}
		int dir=quadrant(p1.getX(),p1.getY(),p2.getX(),p2.getY());
		if(dir==2 || dir==4){
			return Lights.LEFT;
		}
		return Lights.RIGHT;
	}

	/**
	 * 根据点间关系，判断距离是否超过60px,自动添加更加细致的关键点及关系
	 * 
	 * @param dis
	 */
	public static void autoAppendDistance(List<Distance> dis) {
		List<Distance> disToRemove = new ArrayList<Distance>();
		List<Distance> append = new ArrayList<Distance>();
		for (Distance d : dis) {
			if (d.getDistance() >= 2 * MIN_PIXELS_BETWEEN_TWO_POINTS) {
				disToRemove.add(d);
				int t = Math.round(d.getDistance())
						/ MIN_PIXELS_BETWEEN_TWO_POINTS;
				int size = Math.round(d.getDistance()
						% MIN_PIXELS_BETWEEN_TWO_POINTS != 0 ? t + 1 : t);
				Point p1 = d.getPoint1();
				Point p2 = d.getPoint2();
				List<Point> ps = BeanUtils.medianPoints(p1, p2,
						MIN_PIXELS_BETWEEN_TWO_POINTS, size);
				ps.add(0, p1);
				ps.add(p2);
				for (int i = 0; i < ps.size() - 1; i++) {
					append.add(new Distance(ps.get(i), ps.get(i + 1), BeanUtils
							.hypot(ps.get(i), ps.get(i + 1))));
				}
			}
		}
		dis.removeAll(disToRemove);
		dis.addAll(append);
	}

	/**
	 * 根据点于点之间的距离，测算是否超过60px，超过则每30px添加一个中间点
	 * 
	 * @param ps
	 * @return
	 */
	public static List<Point> autoAppendPoint(List<Point> ps) {
		if (ps == null || ps.size() == 0)
			return null;
		List<Point> result = new ArrayList<Point>();
		Point first = ps.get(0);
		for (int i = 0; i < ps.size() - 1; i++) {
			Point p1 = ps.get(i);
			Point p2 = ps.get(i + 1);
			float d = hypot(p1, p2);
			if (d >= MIN_PIXELS_BETWEEN_TWO_POINTS * 2) {
				int t = (int) Math.floor(d / MIN_PIXELS_BETWEEN_TWO_POINTS);
				int size = Math
						.round(d % MIN_PIXELS_BETWEEN_TWO_POINTS != 0 ? t
								: t - 1);
				List<Point> ps2 = BeanUtils.medianPoints(p1, p2,
						MIN_PIXELS_BETWEEN_TWO_POINTS, size);
				ps2.add(p2);
				result.addAll(ps2);
			}
		}
		if (result.size() != 0) {
			result.add(0, first);
		}
		return result.size() == 0 ? ps : result;
	}

	/**
	 * 将不在水平方向的两点调整为水平方向
	 * 
	 * @param p1
	 *            起点
	 * @param p2
	 *            终点
	 * @return
	 */
	@Deprecated
	private static Point resetRoadModel(Point p1, Point p2) {
		int x1 = p1.getX(), y1 = p1.getY(), x2 = p2.getX(), y2 = p2.getY();
		int _hypot_ = (int) Math.hypot(x2 - x1, y2 - y1);
		return new Point(0, x2 + _hypot_, p2.getY(), p2.getArea(), p2.getRoad());
	}

	/**
	 * <span style="color:red"> ******IMPORTANT ALGORITHM******</span><br>
	 ******* 判断点是否在路上******
	 * 
	 * @param dis
	 * @param p
	 * @return
	 */
	public static Distance inRoad(List<Distance> dis, Point p) {
		for (Distance d : dis) {
			Point p1 = d.getPoint1();
			Point p2 = d.getPoint2();
			// 点到直线距离
			double di = pointToLine(p, p1, p2);
			if (di > ROAD_WIDTH / 2) {
				continue;
			}
			double A = calculateAngle(p, p1, p2);
			// 最小角
			Point min = minOrMaxPoint(p1, p2, 0, ROAD_WIDTH / 2);
			double MinAngle = calculateAngle(min, p1, p2);
			if (A >= MinAngle && A <= 180) {
				return d;
			}
		}
		return null;
	}

	/**
	 * 求出构建道路的四边形内的点为顶点与起点终点的夹角产生最大角最小角的两个点坐标
	 * 
	 * @param p1
	 * @param p2
	 * @param gap
	 *            为0时则为最小点，为二分之一起点终点距离时为最大点
	 * @param d2
	 *            为路宽的一半
	 * @return
	 */
	public static Point minOrMaxPoint(Point p1, Point p2, int gap, int d2) {
		int x1 = p1.getX(), y1 = p1.getY(), x2 = p2.getX(), y2 = p2.getY();
		int _hypot_ = (int) Math.hypot(x2 - x1, y2 - y1);
		int x3 = 0, y3 = 0, x4 = 0, y4 = 0;
		switch (quadrant(x1, y1, x2, y2)) {
		case 1:
			x3 = x2 - gap * (x2 - x1) / _hypot_;
			y3 = y2 + (gap * (y1 - y2) / _hypot_);
			x4 = x3 - d2 * (y1 - y2) / _hypot_;
			y4 = y3 - d2 * (x2 - x1) / _hypot_;
			break;
		case 2:
			x3 = x2 + gap * (x1 - x2) / _hypot_;
			y3 = y2 + gap * (y1 - y2) / _hypot_;
			x4 = (x3 + d2 * (y1 - y2) / _hypot_);
			y4 = (y3 - d2 * (x1 - x2) / _hypot_);
			break;
		case 3:
			x3 = x2 + (gap * (x1 - x2) / _hypot_);
			y3 = y2 - (gap * (y2 - y1) / _hypot_);
			x4 = x3 - (d2 * (y2 - y1) / _hypot_);
			y4 = y3 - d2 * (x1 - x2) / _hypot_;
			break;
		case 4:
			x3 = x2 - (gap * (x2 - x1) / _hypot_);
			y3 = y2 - (gap * (y2 - y1) / _hypot_);
			x4 = x3 + d2 * (y2 - y1) / _hypot_;
			y4 = y3 - d2 * (x2 - x1) / _hypot_;
			break;
		}
		return new Point(0, x4, y4, p2.getArea(), p2.getRoad());
	}

	@Deprecated
	public static void createRoadModel(List<Distance> dis) {
		for (Distance distance : dis) {
			Point p1 = distance.getPoint1();
			Point p2 = distance.getPoint2();
			distance.setPoint1(resetRoadModel(p1, p2));
			distance.setX1(p1.getX());
			distance.setY1(p1.getY() + ROAD_WIDTH / 2);
			distance.setX2(p2.getX());
			distance.setY2(p2.getY() - ROAD_WIDTH / 2);
		}
	}

	/**
	 * 已知三点之间的距离求角度
	 * 
	 * @param pp1
	 *            角的对边
	 * @param pp2
	 *            角的邻边
	 * @param p1p2
	 *            角的邻边
	 * @return 角度
	 */
	public static double calculateAngle(float pp1, float pp2, float p1p2) {
		return Math.acos((pp1 * pp1 + pp2 * pp2 - p1p2 * p1p2)
				/ (2 * pp1 * pp2))
				* 180 / Math.PI;
	}

	/**
	 * 以及三点求角度
	 * 
	 * @param p
	 *            角顶点
	 * @param p1
	 *            角射线上的点
	 * @param p2
	 *            角射线上的点
	 * @return 角度
	 */
	public static double calculateAngle(Point p, Point p1, Point p2) {
		return calculateAngle(hypot(p, p1), hypot(p, p2), hypot(p1, p2));
	}

	/**
	 * 点到直线的距离
	 * 
	 * @param p
	 *            点
	 * @param p1
	 *            构成直线的一点
	 * @param p2
	 *            构成直线的一点
	 * @return
	 */
	public static double pointToLine(Point p, Point p1, Point p2) {
		double di = 0;
		if (p1.getX() == p2.getX()) {
			di = Math.abs(p.getX() - p1.getX());
		} else if (p1.getY() == p2.getY()) {
			di = Math.abs(p.getY() - p1.getY());
		} else {
			float y = ((p2.getY() - p1.getY()) / (p2.getX() - p1.getX()))
					* (p.getX() - p1.getX()) + p1.getY();
			float x = ((p2.getX() - p1.getX()) / (p2.getY() - p1.getY()))
					* (p.getY() - p1.getY()) + p1.getX();

			di = Math.abs((p.getX() - x) * (p.getY() - y)
					/ Math.hypot(p.getX() - x, y - p.getY()));

		}
		return Double.isNaN(di) ? 0 : di;
	}

	/**
	 * 将千米每小时转换为像素每秒
	 * 
	 * @param kph
	 * @return
	 */
	public static float kilometerPerHourToPixelPerSecond(int kph) {
		return kph / 3.6f * 2;
	}
}